package com.generator;

import cn.hutool.core.util.StrUtil;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.*;

import java.sql.JDBCType;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * A MyBatis Generator plugin to use Lombok's @Data annoation
 * instead of getters and setters
 *
 * @author Paolo Predonzani (http://softwareloop.com/)
 */
public class JPAPlugin extends PluginAdapter {

    private FullyQualifiedJavaType entity;
    private FullyQualifiedJavaType table;
    private FullyQualifiedJavaType idClass;
    private FullyQualifiedJavaType repository;
    private FullyQualifiedJavaType id;
    private FullyQualifiedJavaType column;

    Map<String,TopLevelClass>  topLevelClassMap = new HashMap<>();

    /**
     * LombokPlugin contructor
     */
    public JPAPlugin() {
        entity = new FullyQualifiedJavaType("javax.persistence.Entity");
        table = new FullyQualifiedJavaType("javax.persistence.Table");
        idClass = new FullyQualifiedJavaType("javax.persistence.IdClass");
        repository = new FullyQualifiedJavaType("org.springframework.data.jpa.repository.JpaRepository");
        id = new FullyQualifiedJavaType("javax.persistence.Id");
        column = new FullyQualifiedJavaType("javax.persistence.Column");
    }

    @Override
    public boolean validate(List<String> warnings) {
        return true;
    }

    @Override
    public void setProperties(Properties properties) {
        super.setProperties(properties);
    }

    /**
     * Intercepts base record class generation
     *
     * @param topLevelClass
     * @param introspectedTable
     * @return
     */
    @Override
    public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass,
                                                 IntrospectedTable introspectedTable) {
        // 添加实体类注解
        topLevelClass.addImportedType(entity);
        topLevelClass.addAnnotation("@Entity");

        // 添加表注解
        topLevelClass.addImportedType(table);
        topLevelClass.addAnnotation("@Table(name = \"" + introspectedTable.getFullyQualifiedTableNameAtRuntime() + "\")");

        topLevelClass.addImportedType(id);

        TopLevelClass pkclass = topLevelClassMap.get(introspectedTable.getFullyQualifiedTableNameAtRuntime());
        if(pkclass != null){
            pkclass.getFields().stream().forEach(f->topLevelClass.addField(f));
            topLevelClass.addImportedType(idClass);
            topLevelClass.addImportedType(topLevelClass.getSuperClass().getFullyQualifiedName());
            topLevelClass.addAnnotation(StrUtil.format("@IdClass({}.class)",topLevelClass.getSuperClass().getShortName()));
            topLevelClass.setSuperClass("com.demo.web.page.entity.DbEntity");
            topLevelClass.addImportedType("com.demo.web.page.entity.DbEntity");
        }
        topLevelClass.addImportedType(column);

        return true;
    }

    @Override
    public boolean modelPrimaryKeyClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        topLevelClass.addImportedType(id);
        topLevelClass.addImportedType(column);
        topLevelClassMap.put(introspectedTable.getFullyQualifiedTableNameAtRuntime(),topLevelClass);
        return true;
    }

    @Override
    public boolean modelFieldGenerated(Field field, TopLevelClass topLevelClass, IntrospectedColumn introspectedColumn, IntrospectedTable introspectedTable, ModelClassType modelClassType) {
        if(introspectedTable.getPrimaryKeyColumns().contains(introspectedColumn)){
            field.addAnnotation("@Id");
        }
        if(introspectedColumn.getActualColumnName().startsWith("c_")){
            field.setName(field.getName().substring(1));
            field.setName(StrUtil.lowerFirst(field.getName()));
        }
        if(introspectedColumn.getJdbcType() == JDBCType.TINYINT.getVendorTypeNumber()){
            field.setType(new FullyQualifiedJavaType("java.lang.Integer"));
        }
        field.addAnnotation(StrUtil.format("@Column(name = \"{}\",nullable = {})",introspectedColumn.getActualColumnName(),introspectedColumn.isNullable()));
        return super.modelFieldGenerated(field, topLevelClass, introspectedColumn, introspectedTable, modelClassType);
    }

    @Override
    public boolean clientGenerated(Interface interfaze, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        interfaze.addImportedType(repository);
        String key = "String";
        TopLevelClass pkclass = topLevelClassMap.get(introspectedTable.getFullyQualifiedTableNameAtRuntime());
        if(pkclass != null){
            key = pkclass.getType().getShortName();
        }
        interfaze.addImportedType(new FullyQualifiedJavaType("com.demo.web.generator.entity.*"));
        interfaze.addSuperInterface(new FullyQualifiedJavaType(StrUtil.format("JpaRepository<{}, {}>",introspectedTable.getTableConfiguration().getDomainObjectName(),key)));
        return true;
    }

    @Override
    public boolean clientCountByExampleMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientCountByExampleMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientDeleteByExampleMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientDeleteByExampleMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientDeleteByPrimaryKeyMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientDeleteByPrimaryKeyMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientInsertMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientInsertMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientInsertSelectiveMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientInsertSelectiveMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientSelectByExampleWithBLOBsMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientSelectByExampleWithBLOBsMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientSelectByExampleWithoutBLOBsMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientSelectByExampleWithoutBLOBsMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientSelectByPrimaryKeyMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientSelectByPrimaryKeyMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientUpdateByExampleSelectiveMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientUpdateByExampleSelectiveMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientUpdateByExampleWithBLOBsMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientUpdateByExampleWithBLOBsMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientUpdateByExampleWithoutBLOBsMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientUpdateByExampleWithoutBLOBsMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientUpdateByPrimaryKeySelectiveMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientUpdateByPrimaryKeySelectiveMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientUpdateByPrimaryKeyWithBLOBsMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientUpdateByPrimaryKeyWithBLOBsMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientUpdateByPrimaryKeyWithoutBLOBsMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        return false;
    }

    @Override
    public boolean clientUpdateByPrimaryKeyWithoutBLOBsMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        return false;
    }
}